//////////////////////////////////////////////////////////////////////////
#pragma once


//////////////////////////////////////////////////////////////////////////
#include <Core/Core.hpp>
#include "InitializerList.hpp"
#include <functional>
#include <unordered_map>


//////////////////////////////////////////////////////////////////////////
namespace Config
{
	//////////////////////////////////////////////////////////////////////////
	typedef std::function< void ( void ) >	Update_t;

	//////////////////////////////////////////////////////////////////////////
	// File system replacement.
	class FileInterface
	{
	public:
		virtual void				Write( const std::string& name, const std::string& data )=0;
		virtual const std::string	Read( const std::string& name )=0;
	};


	//////////////////////////////////////////////////////////////////////////
	// System initializer singleton.  NOT a phoenix style singleton, you need
	// to control when this comes into existence and when it shuts down
	// explicitly.
	// Supplies a registry like system which you can load/save data into.
	// Additional extensions allow for automated load/save of static/global
	// items, ability to update once per frame for items which need to detect
	// changes, and a generalized debug update ability for helpers.
	class Initializer
	{
	public:
		// Construction/Destruction.
		Initializer( const std::string& filename="Config.json", FileInterface* fi=nullptr );
		~Initializer();

		// Primary load/save of configuration.
		void				Load();
		void				Save();

		// Registry.
		void				Initialize( Config::Item& );
		void				Store( Config::Item& );

		// Per-frame update items.
		// For debug/tweaks, anything with a callback which needs to check
		// if it has changed.  Other items also use this such as debug
		// panels in order to update any api driven dynamic data.
		// Given the lack of comparison between functional types, we
		// register via a void pointer to identify the items.
		// This allows you to use "Item*" or whatever other pointer.
		void				Update();
		void				AddUpdater( void*, Update_t );
		void				RemoveUpdater( void* );

	private:
		//
		typedef std::pair< void*, Update_t >					UpdatePair_t;
		typedef std::vector< UpdatePair_t >						UpdateVector_t;
		typedef std::unordered_map< std::string, std::string >	Registry_t;

		//
		void				_ReadRegistry( const JSONArray& );
		std::string			_ItemToString( Item& ) const;
		void				_UpdateInitializers();
		void				_ReformatOutput( std::wstring& ) const;


		//
		std::string			mFilename;
		UpdateVector_t		mUpdatables;
		Registry_t			mRegistry;
		FileInterface*		mpFileInterface;
	};


	//////////////////////////////////////////////////////////////////////////
	extern Initializer*		Instance;
}


//////////////////////////////////////////////////////////////////////////
// Config class organization.
//	Item:				Base concrete class for all tracked items.
//	Serializer:			Type specific serialization injection.
//	TypedItem:			Template class with type reference, default value
//						and generic accessors.
//	InitializerList:	Items which auto serialize on startup and shutdown
//						of the config system.

#define CONFIG_EXTERN( tp, name )						extern tp name;
#define CONFIG_VAR( tp, name, key, defv )				\
	tp name = defv;										\
	Config::InitializerList	name##InitList( key, name );
#define CONFIG_MEMBER( tp, name )						\
	tp name;											\
	Config::InitializerList name##InitList;
#define CONFIG_INIT( name, key, defv )					\
	name( defv ),										\
	name##InitList( key, name )
#define CONFIG_COPY_INIT( name, rhs )					\
	name( rhs.name ),									\
	name##InitList( rhs.name##InitList, name )


//////////////////////////////////////////////////////////////////////////
#if defined( ENABLE_DEVBUILD )

//////////////////////////////////////////////////////////////////////////
// Dev build only items.
#define DEVBUILD( ... )									__VA_ARGS__
#define DEVBUILD_MEMBER( tp, name )						\
	tp name;											\
	Config::InitializerList	name##InitList;
#define DEVBUILD_INIT( name, key, defv )				\
	, name( defv ), name##InitList( key, name )


//////////////////////////////////////////////////////////////////////////
// Tweakables.  In devbuilds these are just configuration items.  In
// final builds they become qualified (i.e. const) types without config
// abilities.
#define TWEAKABLE_VAR( qt, tp, name, key, defv )		CONFIG_VAR( tp, name, key, defv )


//////////////////////////////////////////////////////////////////////////
#else

//////////////////////////////////////////////////////////////////////////
// Disable dev build only items.
#define DEVBUILD( ... )
#define DEVBUILD_MEMBER( tp, name )
#define DEVBUILD_INIT( name, key )


//////////////////////////////////////////////////////////////////////////
// Tweakables.  In devbuilds these are just configuration items.  In
// final builds they become qualified (i.e. const) types without config
// abilities.
#define TWEAKABLE_VAR( qt, tp, name, key, defv )		qt tp name = defv


//////////////////////////////////////////////////////////////////////////
#endif
